/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ie.tcd.cs.railtickets.server.service.impl;

import ie.tcd.cs.railtickets.commons.dto.PriceInfoDTO;
import ie.tcd.cs.railtickets.commons.dto.TicketDTO;
import ie.tcd.cs.railtickets.commons.dto.TimeTableDTO;
import ie.tcd.cs.railtickets.server.dao.AgentDAO;
import ie.tcd.cs.railtickets.server.service.TicketService;
import java.util.Date;
import java.util.List;
import ie.tcd.cs.railtickets.server.dao.TravelPathDAO;
import ie.tcd.cs.railtickets.server.dao.DAOFactory;
import ie.tcd.cs.railtickets.server.dao.PathTimeDAO;
import ie.tcd.cs.railtickets.server.dao.RouteDAO;
import ie.tcd.cs.railtickets.server.dao.RouteStationDAO;
import ie.tcd.cs.railtickets.server.dao.RouteTimeDAO;
import ie.tcd.cs.railtickets.server.dao.SeatStateDAO;
import ie.tcd.cs.railtickets.server.dao.TicketDAO;
import ie.tcd.cs.railtickets.server.domain.Agent;
import ie.tcd.cs.railtickets.server.domain.PathTime;
import ie.tcd.cs.railtickets.server.domain.Route;
import ie.tcd.cs.railtickets.server.domain.RouteStation;
import ie.tcd.cs.railtickets.server.domain.RouteTime;
import ie.tcd.cs.railtickets.server.domain.Ticket;
import ie.tcd.cs.railtickets.server.domain.TravelPath;
import java.util.ArrayList;
import java.util.Calendar;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author Wenbo Sun
 */
public class TicketServiceImpl implements TicketService {

    private TravelPathDAO tpDAO;
    private RouteDAO routeDAO;
    private PathTimeDAO ptDAO;
    private RouteTimeDAO rtDAO;
    private SeatStateDAO ssDAO;
    private RouteStationDAO rsDAO;
    private TicketDAO ticketDAO;
    private AgentDAO agentDAO;

    private static final Log log = LogFactory.getLog(TicketServiceImpl.class);

    public TicketServiceImpl() {
        tpDAO = DAOFactory.createTravelPathDAO();
        routeDAO = DAOFactory.createRouteDAO();
        ptDAO = DAOFactory.createPathTimeDAO();
        rtDAO = DAOFactory.createRouteTimeDAO();
        ssDAO = DAOFactory.createSeatStateDAO();
        rsDAO = DAOFactory.createRouteStationDAO();
        ticketDAO = DAOFactory.createTicketDAO();
        agentDAO = DAOFactory.createAgentDAO();
    }

    public TicketDTO bookTicket(long pathTimeId, Date travelDate, String ipAddr) {
        log.debug("processing ticket booking ...");
        PathTime pathTime = (PathTime) ptDAO.findById(PathTime.class, pathTimeId);

        int seat = this.getAvailSeats(pathTime, travelDate);
        if(seat <= 0 && seat != -1)
            return null;
        
        updateSeats(pathTime, travelDate, -1);

        String ticketNo = java.util.UUID.randomUUID().toString();
        Agent agent = agentDAO.findByAddr(ipAddr);
        log.debug(agent.getName());
        Ticket ticket = new Ticket(ticketNo, pathTime, agent, travelDate, Calendar.getInstance().getTime());
        ticketDAO.save(ticket);

        TicketDTO ticketDTO = new TicketDTO(ticketNo, travelDate);
        return ticketDTO;
    }

    private void updateSeats(PathTime pathTime, Date travelDate, int op) {
        RouteTime routeTime = rtDAO.findByPathTime(pathTime);
        int[] sequences = rsDAO.getSequencesByTime(pathTime);

        Route route = routeDAO.findByPathTime(pathTime);
        List<RouteStation> routeStations = rsDAO.findBy(route, sequences);
        for (RouteStation routeStation : routeStations) {
            if (!ssDAO.isExist(routeTime, travelDate, routeStation)) {
                ssDAO.create(routeTime, travelDate, routeStation);
            }
            ssDAO.bookOrCancel(ssDAO.findBy(routeTime, travelDate, routeStation), op);

        }
    }

    private int getAvailSeats(PathTime pathTime, Date travelDate) {
        RouteTime routeTime = rtDAO.findByPathTime(pathTime);
        int[] sequences = rsDAO.getSequencesByTime(pathTime);
        return ssDAO.findAvailable(routeTime, travelDate, sequences);
    }

    public List<TicketDTO> bookTickets(long pathTimeId, Date travelDate, int sum, String ipAddr) {
        List<TicketDTO> tickets = new ArrayList<TicketDTO>();
        for (int i=0; i< sum; i++)
            tickets.add(this.bookTicket(pathTimeId, travelDate, ipAddr));
        log.debug("book tickets success!");
        return tickets;
    }

    public List<PriceInfoDTO> getPriceInfos(String sourceStation, String destinationStation, Date travelDate) {
        log.debug("processing searching price information ...");
        List<PriceInfoDTO> priceDTOs = new ArrayList<PriceInfoDTO>();
        List<TravelPath> paths = tpDAO.findBy(sourceStation, destinationStation);
        for (TravelPath path : paths) {
            PriceInfoDTO priceDTO = new PriceInfoDTO();
            List<TimeTableDTO> timeDTOs = new ArrayList<TimeTableDTO>();
            Route route = routeDAO.findByPath(path);
            priceDTO.setPathId(path.getId());
            priceDTO.setPrice(path.getPrice());
            priceDTO.setRouteName(route.getRouteName());
            priceDTO.setRouteNo(route.getRouteNo());
            List<PathTime> pathTimes = ptDAO.findByPath(path);
            for (PathTime pathTime : pathTimes) {
                TimeTableDTO timeDTO = new TimeTableDTO();
                timeDTO.setTimeId(pathTime.getId());
                timeDTO.setStartTime(pathTime.getStartTime());
                timeDTO.setTravelPeriod(pathTime.getTravelPeriod());
                int seatAvail = this.getAvailSeats(pathTime, travelDate);
                if (seatAvail == -1) {
                    seatAvail = route.getSeat();
                }
                timeDTO.setSeatAvail(seatAvail);
                timeDTOs.add(timeDTO);
            }
            priceDTO.setTimeTable(timeDTOs);
            priceDTOs.add(priceDTO);
        }
        log.debug("search success!");
        return priceDTOs;
    }

    public boolean cancelTicket(String ticketNo, String ipAddr) {
        log.debug("processing canceling ticket ...");
        if (!ticketDAO.isExist(ticketNo)) {
            return false;
        }
        if(!ticketDAO.confirmBy(ipAddr))
            return false;
        Ticket ticket = ticketDAO.findByNo(ticketNo);
        PathTime pathTime = ptDAO.findByTicket(ticket);
        this.updateSeats(pathTime, ticket.getTravelDate(), 1);
        ticketDAO.delete(ticket);
        log.debug("cancel ticket success!");
        return true;
    }

    public boolean checkAgentExist(String ipAddr) {
        return agentDAO.isExist(ipAddr);
    }
}
